package cogen

import (
	colorstyle "gitee.com/chenwitt/photographcover/color"
	"gitee.com/chenwitt/photographcover/utils"
	"github.com/disintegration/imaging"
	"github.com/fogleman/gg"
	"golang.org/x/image/draw"
	"golang.org/x/image/font"
	"golang.org/x/image/font/opentype"
	"image"
	"image/color"
	"io/ioutil"
)

func CoverGen(opts ...Option) image.Image {
	cfg := NewOptions(opts...)
	dc := gg.NewContext(cfg.Width, cfg.Height)
	dc.SetRGB255(cfg.CoverRGB[0], cfg.CoverRGB[1], cfg.CoverRGB[2])
	dc.Clear()
	fontFace, err := getOpenTypeFontFace(cfg.FontFilePath, cfg.FontSize, cfg.DPI)
	if err != nil {
		panic(err)
	}

	dc.SetFontFace(*fontFace)
	dc.SetRGB255(cfg.FontRGB[0], cfg.FontRGB[1], cfg.FontRGB[2])
	dc.DrawStringWrapped(cfg.Title, cfg.X, cfg.Y, cfg.AX, cfg.AY, cfg.MaxWordWidth, cfg.LineSpacing, gg.AlignCenter)

	// Start compressing images.
	src, err := imaging.Open(cfg.ImagePath)
	if err != nil {
		panic(err)
	}

	src = imaging.Resize(src, cfg.ResizeWidth, cfg.ResizeHeight, imaging.Lanczos)
	dc.DrawImageAnchored(src, cfg.AnchoredX, cfg.AnchoredY, cfg.AnchoredAX, cfg.AnchoredAY)
	//dc.SavePNG(cfg.SavingFileName + ".png")
	return dc.Image()
}

func CoverGen2(opts ...Option) image.Image {
	cfg := NewOptions(opts...)
	dc := gg.NewContext(cfg.Width, cfg.Height)
	dc.SetRGB255(cfg.CoverRGB[0], cfg.CoverRGB[1], cfg.CoverRGB[2])
	dc.Clear()
	fontFace, err := getOpenTypeFontFace2(cfg.FontData, cfg.FontSize, cfg.DPI)
	if err != nil {
		panic(err)
	}
	// 背景图片
	if cfg.BgImage != nil {
		w, h := scale(float64(cfg.Width), float64(cfg.Height), float64(cfg.BgImageConfig.Width), float64(cfg.BgImageConfig.Height))
		bg := resizePic(cfg.BgImage, w, h, true, 255, true)
		dc.DrawImage(bg, 0, 0)
		// 设置字体颜色
		colorCode := colorstyle.ExtractColor(cfg.BgImage)
		fontColor := colorstyle.FontColorMatch(colorCode)
		cfg.FontRGB = colorstyle.TransformColor(fontColor)
	}

	dc.SetFontFace(*fontFace)
	dc.SetRGB255(cfg.FontRGB[0], cfg.FontRGB[1], cfg.FontRGB[2])
	w, _ := dc.MeasureString(cfg.Title)
	// 如果宽度大于了最大宽度，那么就分割字符串，分割处加入空格符号
	if w > cfg.MaxWordWidth {
		strRune := []rune(cfg.Title)
		beishu := int(w/cfg.MaxWordWidth) + 1
		index := len(strRune) / beishu
		for j := 1; j < beishu; j++ {
			inserIndex := j*index + j - 1
			cfg.Title = utils.InsertString(cfg.Title, inserIndex, " ")
		}
	}
	dc.DrawStringWrapped(cfg.Title, cfg.X, cfg.Y, cfg.AX, cfg.AY, cfg.MaxWordWidth, cfg.LineSpacing, gg.AlignCenter)
	return dc.Image()
}

func scale(owidth float64, oheight float64, nwidth float64, nheight float64) (w int, h int) {
	orate := oheight / owidth
	nrate := nheight / nwidth
	if nrate > orate {
		// 如果新图片的高宽比大于画板的高宽比
		rh := owidth * nrate
		return int(owidth), int(rh + 1)
	} else {
		// 否则
		rw := oheight / nrate
		return int(rw + 1), int(oheight)
	}
}

func getOpenTypeFontFace(fontFilePath string, fontSize, dpi float64) (*font.Face, error) {
	fontData, err := ioutil.ReadFile(fontFilePath)
	if err != nil {
		return nil, err
	}
	otfFont, err := opentype.Parse(fontData)
	if err != nil {
		return nil, err
	}
	otfFace, err := opentype.NewFace(otfFont, &opentype.FaceOptions{
		Size: fontSize,
		DPI:  dpi,
	})
	if err != nil {
		return nil, err
	}
	return &otfFace, nil
}

func getOpenTypeFontFace2(fontData []byte, fontSize, dpi float64) (*font.Face, error) {
	otfFont, err := opentype.Parse(fontData)
	if err != nil {
		return nil, err
	}
	otfFace, err := opentype.NewFace(otfFont, &opentype.FaceOptions{
		Size: fontSize,
		DPI:  dpi,
	})
	if err != nil {
		return nil, err
	}
	return &otfFace, nil
}

func resizePic(img image.Image, width int, height int, keepRatio bool, fill int, centerAlign bool) image.Image {
	outImg := image.NewRGBA(image.Rect(0, 0, width, height))
	if !keepRatio {
		draw.BiLinear.Scale(outImg, outImg.Bounds(), img, img.Bounds(), draw.Over, nil)
		return outImg
	}

	if fill != 0 {
		fillColor := color.RGBA{R: uint8(fill), G: uint8(fill), B: uint8(fill), A: 255}
		draw.Draw(outImg, outImg.Bounds(), &image.Uniform{C: fillColor}, image.Point{}, draw.Src)
	}
	dst := calcResizedRect(width, img.Bounds(), height, centerAlign)
	draw.ApproxBiLinear.Scale(outImg, dst.Bounds(), img, img.Bounds(), draw.Over, nil)
	return outImg
}
func calcResizedRect(width int, src image.Rectangle, height int, centerAlign bool) image.Rectangle {
	var dst image.Rectangle
	if width*src.Dy() < height*src.Dx() { // width/src.width < height/src.height
		ratio := float64(width) / float64(src.Dx())

		tH := int(float64(src.Dy()) * ratio)
		pad := 0
		if centerAlign {
			pad = (height - tH) / 2
		}
		dst = image.Rect(0, pad, width, pad+tH)
	} else {
		ratio := float64(height) / float64(src.Dy())
		tW := int(float64(src.Dx()) * ratio)
		pad := 0
		if centerAlign {
			pad = (width - tW) / 2
		}
		dst = image.Rect(pad, 0, pad+tW, height)
	}

	return dst
}
